package com.espirit.common;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import com.espirit.eap.manager.EntityStatus;
import com.espirit.eap.manager.EntityStatusManager;
import com.espirit.eap.manager.ProofNbrRuleManager;
import com.espirit.eap.manager.workflow.FormMessage;
import com.espirit.eap.manager.workflow.WFInstance;
import com.espirit.eap.manager.workflow.WFInstanceService;
import com.espirit.eap.manager.workflow.WFTemplate;
import com.espirit.eap.manager.workflow.WFTemplateService;
import com.espirit.eap.manager.workflow.WorkflowAware;
import com.espirit.eap.manager.workflow.WorkflowServiceAware;
import com.espirit.eap.sa.UserServiceSupport;
import com.espirit.eap.util.StatGenericJPAService;
import com.googlecode.cswish.annotation.GenericType;
import com.googlecode.cswish.annotation.ParamScope;
import com.googlecode.cswish.annotation.Rest;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.model.PageElement;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.Action;
import com.googlecode.cswish.struts.hibernate.HibernateUtil;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.BlankObject;
import com.googlecode.cswish.util.OgnlExUtil;

public abstract class FormService implements WorkflowServiceAware{
	@Resource
	protected StatGenericJPAService genericService;
	
	@Resource
	WFTemplateService wfTemplateService;
	
	@Resource
	WFInstanceService wfInstanceService;
	
	@Resource
	UploadFileService uploadFileService;
	
	@Resource
	OgnlExUtil ognlExUtil;
	
	@Resource
	UserServiceSupport userService;
	
	@Resource
	protected EntityStatusManager entityStatusManager;
	
	@Resource
	protected ObjectFactory objectFactory;
	
	@Resource
	ProofNbrRuleManager proofNbrRuleManager;

	
	 public <T> PageInfo<T> search(T model, int start, int limit,
	    		String sort, String dir, boolean newSearch, Reference<QLInfo> qlInfoRef) {
		 if (StringUtils.isEmpty(sort)){
			 sort = "createdOn" ;
			 dir = "desc";
		 }
		 return genericService.search(model, start, limit, sort, dir, newSearch, qlInfoRef);
	 }
	
	public void _add(Form model , @ParamScope(visible=false, value="#ps")PageElements ps){
		model.setCreatedOn(new Date());
		model.setCreated(userService.getLoginUser());
		setElementProperties(ps);
	}
	
	public Object _update(@GenericType Object model , @ParamScope(visible=false, value="#ps")PageElements ps){
		Form form = (Form) genericService.load(model);
		boolean canUpdate = form.canUpdate();
		
		Collection<PageElement> pageElements = ps.getAllElements().values();
		boolean needSaveButton = false;
		for(PageElement pe : pageElements){
			if (!"hidden".equals(pe.getType()) ) {
				if (canUpdate){
					pe.setTextModel(false);
					needSaveButton = true;
				} else {
					pe.setTextModel(true);
				}
			}
		}
		if (needSaveButton){
			ps.removeExtraParams("disableButtonArea");
		} else {
			ps.addExtraParams("disableButtonArea", false);
		}
		setElementProperties(ps);
    	return genericService.view(model);
	}
	
	private void setElementProperties(PageElements ps){
		ps.setElementReadOnly("model.createdOn", true);
	}
	
	protected abstract boolean preAdd(Form model);
	
	@Transactional
	public void add(@GenericType Object model){
		if (model == null) return ;
		
		Form form = (Form) model;
		
		boolean preAdd = preAdd(form);
		if (!preAdd) return ;
		
		form.setCreatedOn(new Date());
		List<RelatedFile> relatedFiles = form.getRelatedFiles();
		form.setRelatedFiles(null);
		form.setEntityStatus(entityStatusManager.getInit());
		
		String proofNbr = form.getProofNbr();
		if (proofNbr == null){
			proofNbr =  proofNbrRuleManager.getNextNbr(form ,null);
			form.setProofNbr(proofNbr);
		}
		
		form = (Form) genericService.add(form);
		
		postAdd(form);
		
		form.setRelatedFiles(relatedFiles);
		addRelatedFiles(form);
		genericService.merge(form);
	}
	
	protected void postAdd(Form model){
		
	}
	
	protected abstract boolean preUpdate(Form model);
	
	@Transactional
	public void update(@GenericType Object model){
		Form form = (Form) model;
		addRelatedFiles(form);
		
		preUpdate(form);
		
		genericService.update(form);
		
		postUpdate(form);
	}
	
	protected void postUpdate(Form model){
		
	}
	
	private void addRelatedFiles(Form model){
		if (model.getRelatedFiles() != null){
			for(RelatedFile relatedFile : model.getRelatedFiles()){
				relatedFile.setForm(model);
			}
			uploadFileService.setFiles(model.getClass(), model.getRelatedFiles() ,model.getId().toString());
		}
	}
	
	public void _accept(Form model ,@ParamScope(visible=false, value="#ps")PageElements ps ,Reference<Integer> messageId){
    	WFMessage message = (WFMessage)genericService.load(WFMessage.class ,model.getWfMessage().getId());
    	messageId.set(model.getWfMessage().getId());
    	Form form = genericService.load(Form.class , message.getInstance().getSrcId());
    	ognlExUtil.getOgnlUtil().copy(form, model, BlankObject.Map);
    	WFMessage srcMessage = new WFMessage();
    	ognlExUtil.getOgnlUtil().copy(message, srcMessage, BlankObject.Map);
    	WFInstance instance = (WFInstance) HibernateUtil.unproxy(message.getInstance());
    	srcMessage.setInstance(instance);
    	
    	model.setWfMessage(srcMessage);

    	for(PageElement pe : ps.getAllElements().values()){
    		if (pe.getName().startsWith("model.wfMessage")){
    	    	ps.setElementReadOnly("model.wfMessage.initDate", true);
    	    	ps.setElementVisible("model.wfMessage.instance.id", false);
    	    	ps.setElementVisible("model.wfMessage.instance.wfPhase.id", false);
    	    	ps.setElementVisible("model.wfMessage.instance.wfTemplate.id", false);
    	    	ps.setElementReadOnly("model.wfMessage.instance.description", true);
    	    	ps.setElementRequired("model.wfMessage.instance.status.id", true);
    		} else {
    			pe.setTextModel(true);
    		}
    	}
	}
	
	@Transactional
	public void accept(Form model ,Reference<Integer> messageId){
		Map<String, Object> action = new HashMap<String, Object>();
		action.put("model", model);
		model.getWfMessage().setId(messageId.get());
		wfInstanceService.process(model.getWfMessage(), action);
	}
	
//	@Rest(type=Action.ADD)
	public void _goNext(
			@ParamScope(visible = false, value = "#ps") PageElements ps,
			@ParamScope(value = "#fn.newModel('com.espirit.eap.manager.workflow.WFInstance')") WFInstance model,
			List<Form> results) {
		PageElement peCreatedOn = ps.getElement("model.createdOn");
//		ps.setElementVisible( "model.event.id", false);
		ps.setElementVisible( "model.wfTemplate.id", false);
		ps.setElementVisible( "model.createdOn", false);
		ps.setElementVisible( "model.srcId", false);
		ps.setElementVisible( "model.srcClassName", false);

		peCreatedOn.setHidden(true);
		if (results == null) return ;
		Form selectedForm = null;
		for(Form form : results){
			if (form == null || form.getId() == null) continue;
			selectedForm = form;
			break;
		}
		selectedForm = genericService.load(selectedForm);
		
		if (!selectedForm.canRefer()){
			throw new ApplicationException("commons.event.canntGoNext");
		}
		
		WFTemplate template = wfTemplateService.getWFTemplate(selectedForm);
		model.setWfTemplate(template);
		
		model.onSetSrcClassName(selectedForm);
//		model.setSrcClassName(selectedForm.getClass().getName());
		model.setSrcId(selectedForm.getId());
		
		if (template == null){
			ps.addExtraParams("disableButtonArea",true);
			throw new ApplicationException("errors.wf.noTemplate");
		} else {
			ps.removeExtraParams("disableButtonArea");
		}
	}
	
	@Rest(type=Action.ADD)
	@Transactional
	public void goNext(WFInstance model){
		Map<String, Object> action = new HashMap<String, Object>();
		action.put("model", model);
		wfInstanceService.start(model, action);
	}
	
	@Override
	@Transactional
	public void onWFStarted(WorkflowAware aware) {
		EntityStatus refer = entityStatusManager.getRefer();
		Form form = (Form) aware;
		form.onSetEntityStatus(refer);
		genericService.merge(aware);
	}
	
	@Override
	@Transactional
	public void onWFAccept(WorkflowAware aware) {
		EntityStatus accept = entityStatusManager.getAccept();
		Form form = (Form) aware;
		form.onSetEntityStatus(accept);
		genericService.merge(aware);
	}

	@Override
	@Transactional
	public void onWFReject(WorkflowAware aware) {
		EntityStatus reject = entityStatusManager.getReject();
		Form form = (Form) aware;
		form.onSetEntityStatus(reject);
		genericService.merge(aware);
	}
	
	@Override
	public void onWFMessaged(WorkflowAware aware, WFMessage message) {
		FormMessage formMessage = new FormMessage();
		formMessage.setForm((Form)aware);
		formMessage.setMessage(message);
		genericService.add(formMessage);
	}
	
	public void getLayout(@GenericType Object model) {
	}
}
